<!DOCTYPE html>



  


<html class="theme-next muse use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">









<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
















  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />







<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=5.1.4">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=5.1.4">


  <link rel="mask-icon" href="/images/logo.svg?v=5.1.4" color="#222">





  <meta name="keywords" content="Hexo, NexT" />










<meta name="description" content="RegistryDirectory，基于注册中心的服务发现，本文将重点探讨Dubbo是如何实现服务的自动注册与发现。从上篇文章，得知在消息消费者在创建服务调用器(Invoker)【消费者在初始时】时需要根据不同的协议，例如dubbo、registry(从注册中心获取服务提供者)来构建，其调用的方法为Protocol#refer，基于注册中心发现服务提供者的实现协议为RegistryProtocol">
<meta property="og:type" content="article">
<meta property="og:title" content="源码分析Dubbo服务注册与发现机制RegistryDirectory)">
<meta property="og:url" content="https://www.codingw.net/posts/7c2dea71.html">
<meta property="og:site_name" content="中间件兴趣圈">
<meta property="og:description" content="RegistryDirectory，基于注册中心的服务发现，本文将重点探讨Dubbo是如何实现服务的自动注册与发现。从上篇文章，得知在消息消费者在创建服务调用器(Invoker)【消费者在初始时】时需要根据不同的协议，例如dubbo、registry(从注册中心获取服务提供者)来构建，其调用的方法为Protocol#refer，基于注册中心发现服务提供者的实现协议为RegistryProtocol">
<meta property="og:locale">
<meta property="og:image" content="https://img-blog.csdn.net/20180618220050346?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70">
<meta property="og:image" content="https://img-blog.csdn.net/20180618223543475?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70">
<meta property="article:published_time" content="2020-11-04T15:03:01.000Z">
<meta property="article:modified_time" content="2021-04-26T12:09:51.460Z">
<meta property="article:author" content="中间件兴趣圈">
<meta property="article:tag" content="中间件">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://img-blog.csdn.net/20180618220050346?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '',
    scheme: 'Muse',
    version: '5.1.4',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: true,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    duoshuo: {
      userId: '0',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="https://www.codingw.net/posts/7c2dea71.html"/>





  <title>源码分析Dubbo服务注册与发现机制RegistryDirectory) | 中间件兴趣圈</title>
  








<meta name="generator" content="Hexo 5.4.0"></head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/"  class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">中间件兴趣圈</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
      
        <p class="site-subtitle">微信搜『中间件兴趣圈』，回复『Java』获取200本优质电子书</p>
      
  </div>

  <div class="site-nav-toggle">
    <button>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-categories">
          <a href="/categories/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-question-circle"></i> <br />
            
            分类
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-question-circle"></i> <br />
            
            归档
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://www.codingw.net/posts/7c2dea71.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="">
      <meta itemprop="description" content="">
      <meta itemprop="image" content="/images/avatar.gif">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="中间件兴趣圈">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">源码分析Dubbo服务注册与发现机制RegistryDirectory)</h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="创建于" itemprop="dateCreated datePublished" datetime="2020-11-04T23:03:01+08:00">
                2020-11-04
              </time>
            

            

            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/dubbo/" itemprop="url" rel="index">
                    <span itemprop="name">dubbo</span>
                  </a>
                </span>

                
                
              
            </span>
          

          
            
          

          
          
             <span id="/posts/7c2dea71.html" class="leancloud_visitors" data-flag-title="源码分析Dubbo服务注册与发现机制RegistryDirectory)">
               <span class="post-meta-divider">|</span>
               <span class="post-meta-item-icon">
                 <i class="fa fa-eye"></i>
               </span>
               
                 <span class="post-meta-item-text">阅读次数&#58;</span>
               
                 <span class="leancloud-visitors-count"></span>
             </span>
          

          
            <span class="post-meta-divider">|</span>
            <span class="page-pv"><i class="fa fa-file-o"></i>
            <span class="busuanzi-value" id="busuanzi_value_page_pv" ></span>次
            </span>
          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <div id="vip-container"><p>RegistryDirectory，基于注册中心的服务发现，本文将重点探讨Dubbo是如何实现服务的自动注册与发现。从上篇文章，得知在消息消费者在创建服务调用器(Invoker)【消费者在初始时】时需要根据不同的协议，例如dubbo、registry(从注册中心获取服务提供者)来构建，其调用的方法为Protocol#refer，基于注册中心发现服务提供者的实现协议为RegistryProtocol。</p>
<p>RegistryProtocol#refer —-&gt; doRefer方法。</p>
<p>RegistryProtocol#doRefer</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">private &lt;T&gt; Invoker&lt;T&gt; doRefer(Cluster cluster, Registry registry, Class&lt;T&gt; type, URL url) &#123;    &#x2F;&#x2F; @1</span><br><span class="line">        RegistryDirectory&lt;T&gt; directory &#x3D; new RegistryDirectory&lt;T&gt;(type, url);   &#x2F;&#x2F; @2</span><br><span class="line">        directory.setRegistry(registry);</span><br><span class="line">        directory.setProtocol(protocol);   &#x2F;&#x2F; @3</span><br><span class="line">        &#x2F;&#x2F; all attributes of REFER_KEY</span><br><span class="line">        Map&lt;String, String&gt; parameters &#x3D; new HashMap&lt;String, String&gt;(directory.getUrl().getParameters());   &#x2F;&#x2F; @4</span><br><span class="line">        URL subscribeUrl &#x3D; new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);  &#x2F;&#x2F; @5</span><br><span class="line">        if (!Constants.ANY_VALUE.equals(url.getServiceInterface())</span><br><span class="line">                &amp;&amp; url.getParameter(Constants.REGISTER_KEY, true)) &#123;</span><br><span class="line">            registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,</span><br><span class="line">                    Constants.CHECK_KEY, String.valueOf(false)));</span><br><span class="line">        &#125;   &#x2F;&#x2F; @6</span><br><span class="line">        directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,</span><br><span class="line">                Constants.PROVIDERS_CATEGORY</span><br><span class="line">                        + &quot;,&quot; + Constants.CONFIGURATORS_CATEGORY</span><br><span class="line">                        + &quot;,&quot; + Constants.ROUTERS_CATEGORY));     &#x2F;&#x2F; @7</span><br><span class="line"></span><br><span class="line">        Invoker invoker &#x3D; cluster.join(directory);    &#x2F;&#x2F; @8</span><br><span class="line">        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);     &#x2F;&#x2F; @9</span><br><span class="line">        return invoker;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>代码@1：参数详解</p>
<ul>
<li>Cluster cluster：集群策略。</li>
<li> Registry registry：注册中心实现类。</li>
<li>Class<T> type：引用服务名，dubbo:reference interface。</li>
<li>URL url：注册中心URL。</li>
</ul>
<p>代码@2：构建RegistryDirectory对象，基于注册中心动态发现服务提供者（服务提供者新增或减少），本节重点会剖析该类的实现细节。</p>
<p>代码@3：为RegistryDirectory设置注册中心、协议。</p>
<p>代码@4：获取服务消费者的配置属性。</p>
<p>代码@5：构建消费者URL，例如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">consumer:&#x2F;&#x2F;192.168.56.1&#x2F;com.alibaba.dubbo.demo.DemoService?application&#x3D;demo-consumer&amp;check&#x3D;false&amp;dubbo&#x3D;2.0.0&amp;interface&#x3D;com.alibaba.dubbo.demo.DemoService&amp;methods&#x3D;sayHello&amp;pid&#x3D;9892&amp;qos.port&#x3D;33333&amp;side&#x3D;consumer&amp;timestamp&#x3D;1528380277185</span><br></pre></td></tr></table></figure>
<p>代码@6：向注册中心消息消费者：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">consumer:&#x2F;&#x2F;192.168.56.1&#x2F;com.alibaba.dubbo.demo.DemoService?application&#x3D;demo-consumer&amp;category&#x3D;consumers&amp;check&#x3D;false&amp;dubbo&#x3D;2.0.0&amp;interface&#x3D;com.alibaba.dubbo.demo.DemoService&amp;methods&#x3D;sayHello&amp;pid&#x3D;9892&amp;qos.port&#x3D;33333&amp;side&#x3D;consumer&amp;timestamp&#x3D;1528380277185</span><br></pre></td></tr></table></figure>
<p>相比第5步的URL，增加了category=consumers、check=false，其中category表示在注册中心的命名空间，这里代表消费端。该步骤的作用就是向注册中心为服务增加一个消息消费者，其生成的效果如下：【以zookeeper为例】。<br><img src="https://img-blog.csdn.net/20180618220050346?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"><br>代码@7：为消息消费者添加category=providers,configurators,routers属性后，然后向注册中心订阅该URL，关注该服务下的providers,configurators,routers发生变化时通知RegistryDirectory，以便及时发现服务提供者、配置、路由规则的变化。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">consumer:&#x2F;&#x2F;192.168.56.1&#x2F;com.alibaba.dubbo.demo.DemoService?application&#x3D;demo-consumer&amp;category&#x3D;providers,configurators,routers&amp;check&#x3D;false&amp;dubbo&#x3D;2.0.0&amp;interface&#x3D;com.alibaba.dubbo.demo.DemoService&amp;methods&#x3D;sayHello&amp;pid&#x3D;9892&amp;qos.port&#x3D;33333&amp;side&#x3D;consumer&amp;timestamp&#x3D;1528380277185</span><br></pre></td></tr></table></figure>
<p>其订阅关系调用的入口为：RegistryDirectory#subscribe方法，是接下来需要重点分析的重点。</p>
<p>代码@8：根据Directory，利用集群策略返回集群Invoker。</p>
<p>代码@9：缓存服务消费者、服务提供者对应关系。</p>
<p>从这里发现，服务的注册与发现与RegistryDirectory联系非常紧密，接下来让我们来详细分析RegistryDirectory的实现细节。</p>
<h2 id="1、RegistryDirectory类图"><a href="#1、RegistryDirectory类图" class="headerlink" title="1、RegistryDirectory类图"></a>1、RegistryDirectory类图</h2><p><img src="https://img-blog.csdn.net/20180618223543475?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ByZXN0aWdlZGluZw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="这里写图片描述"></p>
<ul>
<li>private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();<pre><code>集群策略，默认为failover。
</code></pre>
</li>
<li>private static final RouterFactory routerFactory = ExtensionLoader.getExtensionLoader (RouterFactory.class).getAdaptiveExtension()路由工厂，可以通过监控中心或治理中心配置。</li>
<li>private static final ConfiguratorFactory configuratorFactory = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class).getAdaptiveExtension();配置实现工厂类。</li>
<li>private final String serviceKey;  服务key，默认为服务接口名。com.alibaba.dubbo.registry.RegistryService，注册中心在Dubbo中也是使用服务暴露。</li>
<li>private final Class&lt; T &gt; serviceType;服务提供者接口类，例如interface com.alibaba.dubbo.demo.DemoService</li>
<li>private final Map&lt; String, String&gt; queryMap：服务消费者URL中的所有属性。</li>
<li>private final URL directoryUrl;注册中心URL，只保留消息消费者URL查询属性，也就是queryMap。</li>
<li>private final String[] serviceMethods：引用服务提供者方法数组。</li>
<li>private final boolean multiGroup：是否引用多个服务组。</li>
<li>private Protocol protocol：协议。</li>
<li>private Registry registry：注册中心实现者。</li>
<li>private volatile List&lt; Configurator&gt; configurators;配置信息。</li>
<li>private volatile Map&lt; String, Invoker&lt; T&gt;&gt; urlInvokerMap; 服务URL对应的Invoker(服务提供者调用器)。</li>
<li>private volatile Map&lt; String, List&lt; Invoker&lt; T&gt;&gt;&gt; methodInvokerMap; methodName : List&lt; Invoker&lt; T &gt;&gt;，<pre><code>   dubbo:method 对应的Invoker缓存表。
</code></pre>
</li>
<li>private volatile Set&lt; URL &gt; cachedInvokerUrls; 当前缓存的所有URL提供者URL。</li>
</ul>
<span id="more"></span>

<h2 id="2、RegistryDirectory-构造方法详解"><a href="#2、RegistryDirectory-构造方法详解" class="headerlink" title="2、RegistryDirectory 构造方法详解"></a>2、RegistryDirectory 构造方法详解</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">public RegistryDirectory(Class&lt;T&gt; serviceType, URL url) &#123;    &#x2F;&#x2F; @1</span><br><span class="line">        super(url);</span><br><span class="line">        if (serviceType &#x3D;&#x3D; null)</span><br><span class="line">            throw new IllegalArgumentException(&quot;service type is null.&quot;);</span><br><span class="line">        if (url.getServiceKey() &#x3D;&#x3D; null || url.getServiceKey().length() &#x3D;&#x3D; 0)</span><br><span class="line">            throw new IllegalArgumentException(&quot;registry serviceKey is null.&quot;);</span><br><span class="line">        this.serviceType &#x3D; serviceType;  </span><br><span class="line">        this.serviceKey &#x3D; url.getServiceKey();     &#x2F;&#x2F; @2</span><br><span class="line">        this.queryMap &#x3D; StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));  &#x2F;&#x2F; @3</span><br><span class="line">        this.overrideDirectoryUrl &#x3D; this.directoryUrl &#x3D; url.setPath(url.getServiceInterface()).clearParameters().addParameters(queryMap).removeParameter(Constants.MONITOR_KEY); &#x2F;&#x2F;@4</span><br><span class="line">        String group &#x3D; directoryUrl.getParameter(Constants.GROUP_KEY, &quot;&quot;);</span><br><span class="line">        this.multiGroup &#x3D; group !&#x3D; null &amp;&amp; (&quot;*&quot;.equals(group) || group.contains(&quot;,&quot;));</span><br><span class="line">        String methods &#x3D; queryMap.get(Constants.METHODS_KEY);</span><br><span class="line">        this.serviceMethods &#x3D; methods &#x3D;&#x3D; null ? null : Constants.COMMA_SPLIT_PATTERN.split(methods);   &#x2F;&#x2F; @5</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>
<p>代码@1：参数描述，serviceType：消费者引用的服务&lt; dubbo:reference interface=”” …/&gt;;URL url：注册中心的URL，例如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">zookeeper:&#x2F;&#x2F;127.0.0.1:2181&#x2F;com.alibaba.dubbo.registry.RegistryService?application&#x3D;demo-consumer&amp;dubbo&#x3D;2.0.0&amp;pid&#x3D;5552&amp;qos.port&#x3D;33333&amp;refer&#x3D;application%3Ddemo-consumer%26check%3Dfalse%26dubbo%3D2.0.0%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D5552%26qos.port%3D33333%26register.ip%3D192.168.56.1%26side%3Dconsumer%26timestamp%3D1528379076123&amp;timestamp&#x3D;1528379076179</span><br></pre></td></tr></table></figure>
<p>代码@2：获取注册中心URL的serviceKey：com.alibaba.dubbo.registry.RegistryService。</p>
<p>代码@3：获取注册中心URL消费提供者的所有配置参数:从url属性的refer。</p>
<p>代码@4：初始化haulovverrideDirecotryUrl、directoryUrl：注册中心的URL，移除监控中心以及其他属性值，只保留消息消费者的配置属性。</p>
<p>代码@5：获取服务消费者单独配置的方法名dubbo:method。</p>
<h2 id="3、RegistryDirectory-subscribe"><a href="#3、RegistryDirectory-subscribe" class="headerlink" title="3、RegistryDirectory#subscribe"></a>3、RegistryDirectory#subscribe</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">public void subscribe(URL url) &#123;</span><br><span class="line">     setConsumerUrl(url);   &#x2F;&#x2F; @1</span><br><span class="line">     registry.subscribe(url, this); &#x2F;&#x2F; @2</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>代码@1：设置RegistryDirectory的consumerUrl为消费者URL。<br>代码@2：调用注册中心订阅消息消息消费者URL，首先看一下接口Registry#subscribe的接口声明：<br>RegistryService:void subscribe(URL url, NotifyListener listener); 这里传入的NotifyListener为RegistryDirectory，其注册中心的subscribe方法暂时不深入去跟踪，不过根据上面URL上面的特点，应该能猜出如下实现关键点：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">consumer:&#x2F;&#x2F;192.168.56.1&#x2F;com.alibaba.dubbo.demo.DemoService?application&#x3D;demo-consumer&amp;category&#x3D;providers,configurators,routers&amp;check&#x3D;false&amp;dubbo&#x3D;2.0.0&amp;interface&#x3D;com.alibaba.dubbo.demo.DemoService&amp;methods&#x3D;sayHello&amp;pid&#x3D;9892&amp;qos.port&#x3D;33333&amp;side&#x3D;consumer&amp;timestamp&#x3D;1528380277185</span><br></pre></td></tr></table></figure>
<ul>
<li>根据消息消费者URL，获取服务名。</li>
<li>根据category=providers、configurators、routers，分别在该服务名下的providers目录、configurators目录、routers目录建立事件监听，监听该目录下节点的创建、更新、删除事件，然后一旦事件触发，将回调RegistryDirectory#void notify(List&lt; URL&gt; urls)。</li>
</ul>
<h2 id="4、RegistryDirectory-notify"><a href="#4、RegistryDirectory-notify" class="headerlink" title="4、RegistryDirectory#notify"></a>4、RegistryDirectory#notify</h2><p>首先该方法是在注册中心providers、configurators、routers目录下的节点发生变化后，通知RegistryDirectory，已便更新最新信息，实现”动态“发现机制。</p>
<p>RegistryDirectory#notify</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">List&lt;URL&gt; invokerUrls &#x3D; new ArrayList&lt;URL&gt;();</span><br><span class="line">List&lt;URL&gt; routerUrls &#x3D; new ArrayList&lt;URL&gt;();</span><br><span class="line">List&lt;URL&gt; configuratorUrls &#x3D; new ArrayList&lt;URL&gt;();</span><br><span class="line">for (URL url : urls) &#123;</span><br><span class="line">     String protocol &#x3D; url.getProtocol();    &#x2F;&#x2F; @1 </span><br><span class="line">     String category &#x3D; url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);   &#x2F;&#x2F; @2</span><br><span class="line">     if (Constants.ROUTERS_CATEGORY.equals(category) || Constants.ROUTE_PROTOCOL.equals(protocol)) &#123;   &#x2F;&#x2F; @3</span><br><span class="line">           routerUrls.add(url);</span><br><span class="line">      &#125; else if (Constants.CONFIGURATORS_CATEGORY.equals(category) || Constants.OVERRIDE_PROTOCOL.equals(protocol)) &#123;   &#x2F;&#x2F; @4</span><br><span class="line">           configuratorUrls.add(url);</span><br><span class="line">     &#125; else if (Constants.PROVIDERS_CATEGORY.equals(category)) &#123;    &#x2F;&#x2F; @5</span><br><span class="line">           invokerUrls.add(url);</span><br><span class="line">     &#125; else &#123;</span><br><span class="line">          logger.warn(&quot;Unsupported category &quot; + category + &quot; in notified url: &quot; + url + &quot; from registry &quot; + getUrl().getAddress() + &quot; to consumer &quot; + </span><br><span class="line">                NetUtils.getLocalHost());</span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Step1：根据通知的URL的前缀，分别添加到：invokerUrls(提供者url)、routerUrls（路由信息）、configuratorUrls （配置url）。</p>
<ul>
<li>代码@1：从url中获取协议字段，例如condition://、route://、script://、override://等。</li>
<li>代码@2：获取url的category,在注册中心的命令空间，例如:providers、configurators、routers。</li>
<li>代码@3：如果category等于routers或协议等于route，则添加到routerUrls中。</li>
<li>代码@4：如果category等于configurators或协议等于override，则添加到configuratorUrls中。</li>
<li>代码@5：如果category等于providers，则表示服务提供者url，加入到invokerUrls中。</li>
</ul>
<p>RegistryDirectory#notify</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; configurators</span><br><span class="line">if (configuratorUrls !&#x3D; null &amp;&amp; !configuratorUrls.isEmpty()) &#123;</span><br><span class="line">    this.configurators &#x3D; toConfigurators(configuratorUrls);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Step2：将configuratorUrls转换为配置对象List&lt; Configurator&gt; configurators，该方法将在《源码分析Dubbo配置规则实现细节》一文中详细讲解。</p>
<p>RegistryDirectory#notify</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; routers</span><br><span class="line">if (routerUrls !&#x3D; null &amp;&amp; !routerUrls.isEmpty()) &#123;</span><br><span class="line">      List&lt;Router&gt; routers &#x3D; toRouters(routerUrls);</span><br><span class="line">      if (routers !&#x3D; null) &#123; &#x2F;&#x2F; null - do nothing</span><br><span class="line">            setRouters(routers);</span><br><span class="line">      &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Step3：将routerUrls路由URL转换为Router对象，该部分内容将在《源码分析Dubbo路由机制实现细节》一文中详细分析。</p>
<p>RegistryDirectory#notify</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; providers</span><br><span class="line">refreshInvoker(invokerUrls);</span><br></pre></td></tr></table></figure>
<p>Step4：根据回调通知刷新服务提供者集合。</p>
<h2 id="5、RegistryDirectory-refreshInvoker"><a href="#5、RegistryDirectory-refreshInvoker" class="headerlink" title="5、RegistryDirectory#refreshInvoker"></a>5、RegistryDirectory#refreshInvoker</h2><p>RegistryDirectory#refreshInvoker</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">if (invokerUrls !&#x3D; null &amp;&amp; invokerUrls.size() &#x3D;&#x3D; 1 &amp;&amp; invokerUrls.get(0) !&#x3D; null</span><br><span class="line">         &amp;&amp; Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) &#123;</span><br><span class="line">     this.forbidden &#x3D; true; &#x2F;&#x2F; Forbid to access</span><br><span class="line">     this.methodInvokerMap &#x3D; null; &#x2F;&#x2F; Set the method invoker map to null</span><br><span class="line">     destroyAllInvokers(); &#x2F;&#x2F; Close all invokers</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure>
<p>Step1：如果invokerUrls不为空并且长度为1，并且协议为empty,表示该服务的所有服务提供者都下线了。需要销毁当前所有的服务提供者Invoker。</p>
<p>RegistryDirectory#refreshInvoker</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">this.forbidden &#x3D; false; &#x2F;&#x2F; Allow to access</span><br><span class="line">Map&lt;String, Invoker&lt;T&gt;&gt; oldUrlInvokerMap &#x3D; this.urlInvokerMap; &#x2F;&#x2F; local reference</span><br><span class="line">if (invokerUrls.isEmpty() &amp;&amp; this.cachedInvokerUrls !&#x3D; null) &#123;</span><br><span class="line">        invokerUrls.addAll(this.cachedInvokerUrls);</span><br><span class="line">&#125; else &#123;</span><br><span class="line">      this.cachedInvokerUrls &#x3D; new HashSet&lt;URL&gt;();</span><br><span class="line">      this.cachedInvokerUrls.addAll(invokerUrls);&#x2F;&#x2F;Cached invoker urls, convenient for comparison</span><br><span class="line">&#125;</span><br><span class="line">if (invokerUrls.isEmpty()) &#123;</span><br><span class="line">      return;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Step2： 如果invokerUrls为空，并且已缓存的invokerUrls不为空，将缓存中的invoker url复制到invokerUrls中，这里可以说明如果providers目录未发送变化，invokerUrls则为空，表示使用上次缓存的服务提供者URL对应的invoker；如果invokerUrls不为空，则用iinvokerUrls中的值替换原缓存的invokerUrls，这里说明，如果providers发生变化，invokerUrls中会包含此时注册中心所有的服务提供者。如果invokerUrls为空，则无需处理，结束本次更新服务提供者Invoker操作。</p>
<p>RegistryDirectory#refreshInvoker</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Map&lt;String, Invoker&lt;T&gt;&gt; newUrlInvokerMap &#x3D; toInvokers(invokerUrls);&#x2F;&#x2F; Translate url list to Invoker map</span><br><span class="line">Map&lt;String, List&lt;Invoker&lt;T&gt;&gt;&gt; newMethodInvokerMap &#x3D; toMethodInvokers(newUrlInvokerMap); &#x2F;&#x2F; Change method name to map Invoker Map</span><br></pre></td></tr></table></figure>
<p>Step3：将invokerUrls转换为对应的Invoke，然后根据服务级的url:invoker映射关系创建method:List&lt; Invoker&gt;映射关系，将在下文相信分析。</p>
<p>RegistryDirectory#refreshInvoker</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">this.methodInvokerMap &#x3D; multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;</span><br><span class="line">this.urlInvokerMap &#x3D; newUrlInvokerMap;</span><br><span class="line">try &#123;</span><br><span class="line">        destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); &#x2F;&#x2F; Close the unused Invoker</span><br><span class="line">&#125; catch (Exception e) &#123;</span><br><span class="line">        logger.warn(&quot;destroyUnusedInvokers error. &quot;, e);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Step4：如果支持multiGroup机制，则合并methodInvoker，将在下文分析，然后根据toInvokers、toMethodInvokers刷新当前最新的服务提供者信息。</p>
<h2 id="6、RegistryDirectory-toInvokers"><a href="#6、RegistryDirectory-toInvokers" class="headerlink" title="6、RegistryDirectory#toInvokers"></a>6、RegistryDirectory#toInvokers</h2><p>RegistryDirectory#toInvokers</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">String queryProtocols &#x3D; this.queryMap.get(Constants.PROTOCOL_KEY);</span><br><span class="line">for (URL providerUrl : urls) &#123;</span><br><span class="line">    &#x2F;&#x2F; ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Step1：获取消息消费者URL中的协议类型，&lt; dubbo:reference protocol=”” …/&gt;属性值，然后遍历所有的Invoker Url(服务提供者URL)。</p>
<p>RegistryDirectory#toInvokers</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">if (queryProtocols !&#x3D; null &amp;&amp; queryProtocols.length() &gt; 0) &#123;</span><br><span class="line">       boolean accept &#x3D; false;</span><br><span class="line">       String[] acceptProtocols &#x3D; queryProtocols.split(&quot;,&quot;);</span><br><span class="line">       for (String acceptProtocol : acceptProtocols) &#123;</span><br><span class="line">            if (providerUrl.getProtocol().equals(acceptProtocol)) &#123;</span><br><span class="line">                  accept &#x3D; true;</span><br><span class="line">                  break;</span><br><span class="line">            &#125;</span><br><span class="line">       &#125;</span><br><span class="line">      if (!accept) &#123;</span><br><span class="line">            continue;</span><br><span class="line">      &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Step2： 从这一步开始，代码都包裹在for(URL providerUrl : urls)中，一个一个处理提供者URL。如果dubbo:referecnce标签的protocol不为空，则需要对服务提供者URL进行过滤，匹配其协议与protocol属性相同的服务，如果不匹配，则跳过后续处理逻辑，接着处理下一个服务提供者URL。</p>
<p>RegistryDirectory#toInvokers</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) &#123;</span><br><span class="line">      continue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Step3：如果协议为empty，跳过，处理下一个服务提供者URL。</p>
<p>RegistryDirectory#toInvokers</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) &#123;</span><br><span class="line">       logger.error(new IllegalStateException(&quot;Unsupported protocol &quot; + providerUrl.getProtocol() + &quot; in notified url: &quot; + providerUrl + &quot; from registry &quot; + getUrl().getAddress() + &quot; to </span><br><span class="line">                 consumer &quot; + NetUtils.getLocalHost()</span><br><span class="line">                        + &quot;, supported protocol: &quot; + ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));</span><br><span class="line">   continue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Step4：验证服务提供者协议，如果不支持，则跳过。</p>
<p>RegistryDirectory#toInvokers</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">URL url &#x3D; mergeUrl(providerUrl);</span><br></pre></td></tr></table></figure>
<p>Step5：合并URL中的属性，其具体实现细节如下：</p>
<ol>
<li>消费端属性覆盖生产者端属性（配置属性消费者端优先生产者端属性），其具体实现方法：ClusterUtils.mergeUrl(providerUrl, queryMap)，其中queryMap为消费端属性。<br>  a、首先移除只在服务提供者端生效的属性（线程池相关）：threadname、default.threadname、threadpool、default.threadpool、corethreads、default.corethreads、threads、default.threads、queues、default.queues、alive、default.alive、transporter、default.transporter，服务提供者URL中的这些属性来源于dubbo:protocol、dubbo:provider。<br> b、用消费端配置属性覆盖服务端属性。<br> c、如下属性以服务端优先：dubbo(dubbo信息)、version（版本）、group（服务组）、methods（服务方法）、timestamp（时间戳）。<br> d、合并服务端，消费端Filter,其配置属性（reference.filter），返回结果为：provider#reference.filter,<br> consumer#reference.filter。<br> e、合并服务端，消费端Listener，其配置属性(invoker.listener)，返回结果为：provider#invoker.listener，consumer#invoker.listener。</li>
<li>合并configuratorUrls 中的属性，我们现在应该知道，dubbo可以在监控中心或管理端(dubbo-admin)覆盖覆盖服务提供者的属性，其使用协议为override，该部分的实现逻辑见：《源码分析Dubbo配置规则机制（override协议）》</li>
<li>为服务提供者URL增加check=false，默认只有在服务调用时才检查服务提供者是否可用。</li>
<li>重新复制overrideDirectoryUrl，providerUrl在进过第一步参数合并后（包含override协议覆盖后的属性）赋值给overrideDirectoryUrl。<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">String key &#x3D; url.toFullString(); &#x2F;&#x2F; The parameter urls are sorted</span><br><span class="line">if (keys.contains(key)) &#123; &#x2F;&#x2F; Repeated url</span><br><span class="line">      continue;</span><br><span class="line">&#125;</span><br><span class="line">keys.add(key);</span><br></pre></td></tr></table></figure>
Step6：获取url所有属性构成的key,该key也是RegistryDirectory中Map&lt;String, Invoker<T>&gt; urlInvokerMap;中的key。</li>
</ol>
<p>RegistryDirectory#toInvokers</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">Map&lt;String, Invoker&lt;T&gt;&gt; localUrlInvokerMap &#x3D; this.urlInvokerMap; &#x2F;&#x2F; local reference</span><br><span class="line">Invoker&lt;T&gt; invoker &#x3D; localUrlInvokerMap &#x3D;&#x3D; null ? null : localUrlInvokerMap.get(key);</span><br><span class="line">if (invoker &#x3D;&#x3D; null) &#123; &#x2F;&#x2F; Not in the cache, refer again</span><br><span class="line">   try &#123;</span><br><span class="line">         boolean enabled &#x3D; true;</span><br><span class="line">         if (url.hasParameter(Constants.DISABLED_KEY)) &#123;</span><br><span class="line">               enabled &#x3D; !url.getParameter(Constants.DISABLED_KEY, false);</span><br><span class="line">          &#125; else &#123;</span><br><span class="line">               enabled &#x3D; url.getParameter(Constants.ENABLED_KEY, true);</span><br><span class="line">           &#125;</span><br><span class="line">           if (enabled) &#123;</span><br><span class="line">                invoker &#x3D; new InvokerDelegate&lt;T&gt;(protocol.refer(serviceType, url), url, providerUrl);</span><br><span class="line">           &#125;</span><br><span class="line">     &#125; catch (Throwable t) &#123;</span><br><span class="line">               logger.error(&quot;Failed to refer invoker for interface:&quot; + serviceType + &quot;,url:(&quot; + url + &quot;)&quot; + t.getMessage(), t);</span><br><span class="line">      &#125;</span><br><span class="line">      if (invoker !&#x3D; null) &#123; &#x2F;&#x2F; Put new invoker in cache</span><br><span class="line">            newUrlInvokerMap.put(key, invoker);</span><br><span class="line">      &#125;</span><br><span class="line">&#125; else &#123;</span><br><span class="line">       newUrlInvokerMap.put(key, invoker);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Step7：如果localUrlInvokerMap中未包含invoker并且该provider状态为启用，则创建该URL对应的Invoker，并添加到newUrlInvokerMap中。toInvokers运行结束后，回到refreshInvoker方法中继续往下执行，根据 最新的服务提供者映射关系Map&lt; String,Invoker&gt;，构建Map&lt; String,List&lt; Invoker&gt;&gt;,其中键为methodName。然后更新RegistryDirectory的urlInvokerMap、methodInvokerMap属性，并销毁老的Invoker对象，完成一次路由发现过程。</p>
<p>上面整个过程完成了一次动态服务提供者发现流程，下面再分析一下RegistryDirectory的另外一个重要方法，doList，再重复一遍RegistryDirectory的作用，服务提供者目录服务，在集群Invoker的实现中，内部持有一个Direcotry对象，在进行服务调用之前，首先先从众多的Invoker中选择一个来执行，那众多的Invoker从哪来呢？其来源于集群Invoker中会调用Direcotry的public List&lt; Invoker&lt; T&gt;&gt; list(Invocation invocation)，首先将调用AbstractDirectory#list方法，然后再内部调用doList方法，doList方法有其子类实现。</p>
<h2 id="7、RegistryDirectory-doList-Invocation-invocation-方法详解"><a href="#7、RegistryDirectory-doList-Invocation-invocation-方法详解" class="headerlink" title="7、RegistryDirectory#doList(Invocation invocation) 方法详解"></a>7、RegistryDirectory#doList(Invocation invocation) 方法详解</h2><p>RegistryDirectory#doList</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">if (forbidden) &#123;</span><br><span class="line">      &#x2F;&#x2F; 1. No service provider 2. Service providers are disabled</span><br><span class="line">      throw new RpcException(RpcException.FORBIDDEN_EXCEPTION,</span><br><span class="line">            &quot;No provider available from registry &quot; + getUrl().getAddress() + &quot; for service &quot; + getConsumerUrl().getServiceKey() + &quot; on consumer &quot; +  NetUtils.getLocalHost()</span><br><span class="line">                  + &quot; use dubbo version &quot; + Version.getVersion() + &quot;, please check status of providers(disabled, not registered or in blacklist).&quot;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Step1：如果禁止访问（如果没有服务提供者，或服务提供者被禁用），则抛出没有提供者异常。</p>
<p>RegistryDirectory#doList</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">Map&lt;String, List&lt;Invoker&lt;T&gt;&gt;&gt; localMethodInvokerMap &#x3D; this.methodInvokerMap; &#x2F;&#x2F; local reference</span><br><span class="line">if (localMethodInvokerMap !&#x3D; null &amp;&amp; localMethodInvokerMap.size() &gt; 0) &#123;</span><br><span class="line">    String methodName &#x3D; RpcUtils.getMethodName(invocation);</span><br><span class="line">    Object[] args &#x3D; RpcUtils.getArguments(invocation);</span><br><span class="line">    if (args !&#x3D; null &amp;&amp; args.length &gt; 0 &amp;&amp; args[0] !&#x3D; null</span><br><span class="line">             &amp;&amp; (args[0] instanceof String || args[0].getClass().isEnum())) &#123;</span><br><span class="line">          invokers &#x3D; localMethodInvokerMap.get(methodName + &quot;.&quot; + args[0]); &#x2F;&#x2F; The routing can be enumerated according to the first parameter</span><br><span class="line">     &#125;</span><br><span class="line">    if (invokers &#x3D;&#x3D; null) &#123;</span><br><span class="line">          invokers &#x3D; localMethodInvokerMap.get(methodName);</span><br><span class="line">    &#125;</span><br><span class="line">    if (invokers &#x3D;&#x3D; null) &#123;</span><br><span class="line">          invokers &#x3D; localMethodInvokerMap.get(Constants.ANY_VALUE);</span><br><span class="line">     &#125;</span><br><span class="line">     if (invokers &#x3D;&#x3D; null) &#123;</span><br><span class="line">         Iterator&lt;List&lt;Invoker&lt;T&gt;&gt;&gt; iterator &#x3D; localMethodInvokerMap.values().iterator();</span><br><span class="line">         if (iterator.hasNext()) &#123;</span><br><span class="line">              invokers &#x3D; iterator.next();</span><br><span class="line">          &#125;</span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br><span class="line">return invokers &#x3D;&#x3D; null ? new ArrayList&lt;Invoker&lt;T&gt;&gt;(0) : invokers;</span><br></pre></td></tr></table></figure>
<p>Step2：根据方法名称，从Map&lt; String,List&lt; Invoker&gt;&gt;这个集合中找到合适的List&lt; Invoker&gt;，如果方法名未命中，则返回所有的Invoker，localMethodInvokerMap中方法名，主要是dubbo:service的子标签dubbo:method，最终返回invokers。</p>
<p>本文详细介绍了服务消费者基于注册中心的服务发现机制，其中对routers(路由)与configurators(override协议)并未详细展开，下节先重点分析configurators与routers(路由)实现细节。</p>
<p>总结一下服务注册与发现机制：<br>基于注册 中心的事件通知（订阅与发布），一切支持事件订阅与发布的框架都可以作为Dubbo注册中心的选型。</p>
<ol>
<li><p>服务提供者在暴露服务时，会向注册中心注册自己，具体就是在${service interface}/providers目录下添加 一个节点（临时），服务提供者需要与注册中心保持长连接，一旦连接断掉（重试连接）会话信息失效后，注册中心会认为该服务提供者不可用（提供者节点会被删除）。</p>
</li>
<li><p>消费者在启动时，首先也会向注册中心注册自己，具体在${interface interface}/consumers目录下创建一个节点。</p>
</li>
<li><p>消费者订阅${service interface}/ [  providers、configurators、routers ]三个目录，这些目录下的节点删除、新增事件都胡通知消费者，根据通知，重构服务调用器(Invoker)。</p>
</li>
</ol>
<p>以上就是Dubbo服务注册与动态发现机制的原理与实现细节。</p>
</div>

			<script src="https://my.openwrite.cn/js/readmore.js" type="text/javascript"></script>
			<script>
			var isMobile = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i);
			if (!isMobile) {
			    var btw = new BTWPlugin();
			    btw.init({
			        "id": "vip-container",
			        "blogId": "18019-1573088808868-542",
			        "name": "中间件兴趣圈",
			        "qrcode": "https://img-blog.csdnimg.cn/20190314214003962.jpg",
			        "keyword": "more"
			    });
			}
			</script>
		
      
    </div>
    
    
    

    

    

    

    <footer class="post-footer">
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/posts/398539d9.html" rel="next" title="源码分析Dubbo负载算法">
                <i class="fa fa-chevron-left"></i> 源码分析Dubbo负载算法
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/posts/cc583494.html" rel="prev" title="源码分析Dubbo服务消费端启动流程">
                源码分析Dubbo服务消费端启动流程 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          


          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <p class="site-author-name" itemprop="name"></p>
              <p class="site-description motion-element" itemprop="description"></p>
          </div>

          <nav class="site-state motion-element">

            
              <div class="site-state-item site-state-posts">
              
                <a href="/archives/">
              
                  <span class="site-state-item-count">139</span>
                  <span class="site-state-item-name">日志</span>
                </a>
              </div>
            

            
              
              
              <div class="site-state-item site-state-categories">
                <a href="/categories/index.html">
                  <span class="site-state-item-count">18</span>
                  <span class="site-state-item-name">分类</span>
                </a>
              </div>
            

            

          </nav>

          

          

          
          

          
          

          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#1%E3%80%81RegistryDirectory%E7%B1%BB%E5%9B%BE"><span class="nav-number">1.</span> <span class="nav-text">1、RegistryDirectory类图</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2%E3%80%81RegistryDirectory-%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95%E8%AF%A6%E8%A7%A3"><span class="nav-number">2.</span> <span class="nav-text">2、RegistryDirectory 构造方法详解</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3%E3%80%81RegistryDirectory-subscribe"><span class="nav-number">3.</span> <span class="nav-text">3、RegistryDirectory#subscribe</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#4%E3%80%81RegistryDirectory-notify"><span class="nav-number">4.</span> <span class="nav-text">4、RegistryDirectory#notify</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#5%E3%80%81RegistryDirectory-refreshInvoker"><span class="nav-number">5.</span> <span class="nav-text">5、RegistryDirectory#refreshInvoker</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#6%E3%80%81RegistryDirectory-toInvokers"><span class="nav-number">6.</span> <span class="nav-text">6、RegistryDirectory#toInvokers</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#7%E3%80%81RegistryDirectory-doList-Invocation-invocation-%E6%96%B9%E6%B3%95%E8%AF%A6%E8%A7%A3"><span class="nav-number">7.</span> <span class="nav-text">7、RegistryDirectory#doList(Invocation invocation) 方法详解</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">中间件兴趣圈</span>

  
</div>


  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Muse</a> v5.1.4</div>




        
<div class="busuanzi-count">
  <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>

  
    <span class="site-uv">
      <i class="fa fa-user"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
      
    </span>
  

  
    <span class="site-pv">
      <i class="fa fa-eye"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
      
    </span>
  
</div>








        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  












  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
  

  
  
    <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>



  
  

  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>



  


  




	





  





  












  





  

  
  <script src="https://cdn1.lncld.net/static/js/av-core-mini-0.6.4.js"></script>
  <script>AV.initialize("NNEhOL0iOcflg8f1U3HUqiCq-gzGzoHsz", "7kSmkbbb3DktmHALlShDsBUF");</script>
  <script>
    function showTime(Counter) {
      var query = new AV.Query(Counter);
      var entries = [];
      var $visitors = $(".leancloud_visitors");

      $visitors.each(function () {
        entries.push( $(this).attr("id").trim() );
      });

      query.containedIn('url', entries);
      query.find()
        .done(function (results) {
          var COUNT_CONTAINER_REF = '.leancloud-visitors-count';

          if (results.length === 0) {
            $visitors.find(COUNT_CONTAINER_REF).text(0);
            return;
          }

          for (var i = 0; i < results.length; i++) {
            var item = results[i];
            var url = item.get('url');
            var time = item.get('time');
            var element = document.getElementById(url);

            $(element).find(COUNT_CONTAINER_REF).text(time);
          }
          for(var i = 0; i < entries.length; i++) {
            var url = entries[i];
            var element = document.getElementById(url);
            var countSpan = $(element).find(COUNT_CONTAINER_REF);
            if( countSpan.text() == '') {
              countSpan.text(0);
            }
          }
        })
        .fail(function (object, error) {
          console.log("Error: " + error.code + " " + error.message);
        });
    }

    function addCount(Counter) {
      var $visitors = $(".leancloud_visitors");
      var url = $visitors.attr('id').trim();
      var title = $visitors.attr('data-flag-title').trim();
      var query = new AV.Query(Counter);

      query.equalTo("url", url);
      query.find({
        success: function(results) {
          if (results.length > 0) {
            var counter = results[0];
            counter.fetchWhenSave(true);
            counter.increment("time");
            counter.save(null, {
              success: function(counter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(counter.get('time'));
              },
              error: function(counter, error) {
                console.log('Failed to save Visitor num, with error message: ' + error.message);
              }
            });
          } else {
            var newcounter = new Counter();
            /* Set ACL */
            var acl = new AV.ACL();
            acl.setPublicReadAccess(true);
            acl.setPublicWriteAccess(true);
            newcounter.setACL(acl);
            /* End Set ACL */
            newcounter.set("title", title);
            newcounter.set("url", url);
            newcounter.set("time", 1);
            newcounter.save(null, {
              success: function(newcounter) {
                var $element = $(document.getElementById(url));
                $element.find('.leancloud-visitors-count').text(newcounter.get('time'));
              },
              error: function(newcounter, error) {
                console.log('Failed to create');
              }
            });
          }
        },
        error: function(error) {
          console.log('Error:' + error.code + " " + error.message);
        }
      });
    }

    $(function() {
      var Counter = AV.Object.extend("Counter");
      if ($('.leancloud_visitors').length == 1) {
        addCount(Counter);
      } else if ($('.post-title-link').length > 1) {
        showTime(Counter);
      }
    });
  </script>



  

  

  
  

  

  

  

</body>
</html>
